package timing.ukulele.storage.jwt;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.io.Decoders;
import io.jsonwebtoken.security.Keys;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Example;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.stereotype.Component;
import timing.ukulele.storage.entity.UserEntity;
import timing.ukulele.storage.repository.UserRepository;

import javax.annotation.PostConstruct;
import java.security.Key;
import java.util.*;

@Component
@Slf4j
public class JWTProvider {
    private Key key;    // 私钥
    private long tokenValidityInMilliseconds; // 有效时间
    private long tokenValidityInMillisecondsForRememberMe; // 记住我有效时间
    @Autowired
    private JJWTProperties jjwtProperties; // jwt配置参数
    @Autowired
    private UserRepository userRepository;

    @PostConstruct
    public void init() {
        byte[] keyBytes;
        log.debug("Using a Base64-encoded JWT secret key");
        keyBytes = Decoders.BASE64.decode(jjwtProperties.getBase64Secret());
        this.key = Keys.hmacShaKeyFor(keyBytes); // 使用mac-sha算法的密钥
        this.tokenValidityInMilliseconds =
                1000 * jjwtProperties.getTokenValidityInSeconds();
        this.tokenValidityInMillisecondsForRememberMe =
                1000 * jjwtProperties.getTokenValidityInSecondsForRememberMe();
    }

    public String createToken(Authentication authentication, boolean rememberMe) {
        long now = (new Date()).getTime();
        Date validity;
        if (rememberMe) {
            validity = new Date(now + this.tokenValidityInMillisecondsForRememberMe);
        } else {
            validity = new Date(now + this.tokenValidityInMilliseconds);
        }
        User user = this.getUser(authentication.getName());
        if (user == null)
            return null;
        Map<String, Object> map = new HashMap<>();
        map.put("sub", authentication.getName());
        user.eraseCredentials();
        map.put("user", user);

        return Jwts.builder()
                .setClaims(map) // 添加body
                .signWith(key, SignatureAlgorithm.HS512) // 指定摘要算法
                .setExpiration(validity) // 设置有效时间
                .compact();
    }

    public Authentication getAuthentication(String token) {
        Claims claims = Jwts.parserBuilder()
                .setSigningKey(key)
                .build()
                .parseClaimsJws(token).getBody(); // 根据token获取body
        User principal = this.getUser(claims.getSubject());
        if (principal == null)
            return null;
        Collection<? extends GrantedAuthority> authorities;
        authorities = principal.getAuthorities();
        return new UsernamePasswordAuthenticationToken(principal, token, authorities);
    }

    public Claims getUserFromToken(String token){
        Claims claims = Jwts.parserBuilder()
                .setSigningKey(key)
                .build()
                .parseClaimsJws(token).getBody(); // 根据token获取body
        return claims;
    }
    private User getUser(String account) {
        UserEntity filter = new UserEntity();
        filter.setActive(Boolean.TRUE);
        filter.setAccount(account);
        filter.setRole(null);
        Optional<UserEntity> optional = userRepository.findOne(Example.of(filter));
        if (!optional.isPresent())
            return null;
        UserEntity entity = optional.get();
        return new User(entity.getAccount(), entity.getPassword(), Collections.singleton(new SimpleGrantedAuthority(entity.getRole())));
    }

}